﻿/**	\file		time_types.h
 *	\date		(2005-11-08 21:05:37)/(2014-11-24 14:58:19)
 *-----------------------------------------------------------------------------
 *	\brief		时间相关的东西.
 *	\version	1.0.25.47
 *	\author		Nick Shallery	(nicknide@gmail.com)
 *	\copyright	YOU HAVE ALL LEFT WITH THIS CODE WHILE YOU KEEP COMMENT IN EVERY COPY.
 *-----------------------------------------------------------------------------
**/


#if			!defined(__LOLIX__TOY__TIME_TYPES__K_IN_G__)
#define		__LOLIX__TOY__TIME_TYPES__K_IN_G__

#if			!defined(__LOLIX__LOLI_X_CFG__K_IN_G__)
	#include	"../lolix_cfg.h"
#endif	//	!defined(__LOLIX__LOLI_X_CFG__K_IN_G__)

#if			!defined(__LOLIX__LOLIX_ERR__K_IN_G__)
	#include	"../lolix_err.h"
#endif	//	!defined(__LOLIX__LOLIX_ERR__K_IN_G__)

#if			!defined(__LOLIX__LOLI_X_MACRO__K_IN_G__)
	#include	"../lolix_macro.h"
#endif	//	!defined(__LOLIX__LOLI_X_MACRO__K_IN_G__)

#if			!defined(__LOLIX__LOLIX_TYPE__K_IN_G__)
	#include	"../lolix_type.h"
#endif	//	!defined(__LOLIX__LOLIX_TYPE__K_IN_G__)

#undef	min
namespace lolix{namespace toy{
	class	time_type;
	class	time_span;
	class	time_info;
}}



class	LOLIX_NO_VTABLE	lolix::toy::time_info
	: public lolix::itf
{
public:
	LOLIX_DEFINE_FACTORY((0x5a0a824f, 0x2a4e4d2a, 0x8e339592, 0xe5610f35), (time_info** out_ptr));
	LOLIX_DECLARE_ITF(lolix::toy::time_info, lolix::itf, (0xf2ee642d, 0xd69f4e4b, 0xa1b9b048, 0x4aa4999b));
	typedef	lx_i64	tick_type;
	typedef	lx_i64	span_type;
	/**
	 * \fn	lolix::LOLIX_RETNV lolix::toy::time_info::get_tick(tick_type* out_tick, span_type time_skip)const;
	 *
	 * \brief 得到当前时钟的周期
	 *
	 * \param [out] out_tick	得到对应时间的tick
	 * \param time_skip			所求的经过的时间(以ns为单位）
	 *
	 * \return 成功返回
	**/
	LOLIX_INTERFACE(get_tick	, LOLIX_RETNV	, (tick_type* out_tick, span_type time_skip)const);
	//	得到当前时钟相对周期
	LOLIX_INTERFACE(cur_tick	, LOLIX_RETNV	, (tick_type* time_tick)const);
	LOLIX_INTERFACE(get_span	, LOLIX_RETNV	, (span_type* out_tick, tick_type tick)const);
};

namespace lolix{namespace toy{namespace _help{namespace _time_type{
	//	从一个原始类型得到返回类型和计算类型
	template<typename _Ty>class change_type			{public:	typedef _Ty			ret_type;	typedef _Ty			cpt_type;};
#define		__LOLIX__CHANGE_TYPE__TMP_MACRO__(type)						\
	template<>class change_type<type>		{public:	typedef type		ret_type;	typedef long long	cpt_type;};
	__LOLIX__CHANGE_TYPE__TMP_MACRO__(void);
	__LOLIX__CHANGE_TYPE__TMP_MACRO__(float);
	__LOLIX__CHANGE_TYPE__TMP_MACRO__(double);
	__LOLIX__CHANGE_TYPE__TMP_MACRO__(long double);
#undef		__LOLIX__CHANGE_TYPE__TMP_MACRO__

	template<typename _Tp>	struct _cast_type{typedef _Tp rtv;};
#define		__LOLIX__CAST_TYPE__TMP_MACRO__(type)	\
	template<>					struct _cast_type<type>{ typedef long long rtv; };
	__LOLIX__CAST_TYPE__TMP_MACRO__(signed char);
	__LOLIX__CAST_TYPE__TMP_MACRO__(unsigned char);
	__LOLIX__CAST_TYPE__TMP_MACRO__(signed short);
	__LOLIX__CAST_TYPE__TMP_MACRO__(unsigned short);
	__LOLIX__CAST_TYPE__TMP_MACRO__(signed int);
	__LOLIX__CAST_TYPE__TMP_MACRO__(unsigned int);
	__LOLIX__CAST_TYPE__TMP_MACRO__(signed long);
	__LOLIX__CAST_TYPE__TMP_MACRO__(unsigned long);
#undef		__LOLIX__CAST_TYPE__TMP_MACRO__

	//	从一个类型解析出返回类型和计算类型
	struct is_time_type_owner_type{};

	template<bool use_self, typename _Ty>
	struct get_type
	{
		typedef _Ty ret_type;
		static const typename change_type<_Ty>::cpt_type mul_dat = 1;
	};

	template<typename _Ty>
	struct get_type<true, _Ty>
		: public _Ty
	{
		typedef typename _Ty::ret_type ret_type;
		//static const typename change_type<_Ty>::cpt_type mul_dat = _Ty::mul_dat;
	};

	template<typename T>
	struct trans_type
		: public get_type<type::type_inherit<is_time_type_owner_type, T>::value, T>
	{
	private:
		typedef get_type<type::type_inherit<is_time_type_owner_type, T>::value, T> base_type;
	public:
		typedef typename base_type::ret_type ret_type;
		typedef typename type::type_choise<type::type_inherit<is_time_type_owner_type, T>::value
			, type_true
			, type_false
		>::type _own_type;
	};
}}}}


namespace lolix{namespace toy{
#pragma	pack(push, 8)
class time_span
{
	typedef time_span			this_type;
	struct	no_usd;


	//	从一个类型解析出返回类型和计算类型

public:
#define		__NICK_LIB__DEF_TIME_TYPE__(time_name, Mul_data)	\
	template<typename OUT_TYPE>	struct time_name : public _help::_time_type::is_time_type_owner_type{typedef typename _help::_time_type::change_type<OUT_TYPE>::ret_type ret_type;static const typename _help::_time_type::change_type<OUT_TYPE>::cpt_type mul_dat = Mul_data;	time_name(void){}explicit time_name(OUT_TYPE v): _t_val(v){}OUT_TYPE	_t_val;}
	__NICK_LIB__DEF_TIME_TYPE__(us	, (long long)1);
	__NICK_LIB__DEF_TIME_TYPE__(ms	, (long long)1*1000);
	__NICK_LIB__DEF_TIME_TYPE__(s	, (long long)1*1000*1000);
	__NICK_LIB__DEF_TIME_TYPE__(min	, (long long)1*1000*1000*60);
	__NICK_LIB__DEF_TIME_TYPE__(hour, (long long)1*1000*1000*60*60);
	__NICK_LIB__DEF_TIME_TYPE__(day	, (long long)1*1000*1000*60*60*24);
	__NICK_LIB__DEF_TIME_TYPE__(week, (long long)1*1000*1000*60*60*24*7);
#undef		__NICK_LIB__DEF_TIME_TYPE__
private:

	typedef	time_info::tick_type	tick_type;
public:
	typedef	time_info::span_type	span_type;
	struct	time_max{ const static ptrdiff_type time_ptr = 3;operator toy::time_span const* const(void)const{return reinterpret_cast<const toy::time_span*>(time_ptr);}};
	struct	time_zero{ const static ptrdiff_type time_ptr = 7;operator toy::time_span const* const(void)const{return reinterpret_cast<const toy::time_span*>(time_ptr);}};
	struct	time_min{ const static ptrdiff_type time_ptr = 11;operator toy::time_span const* const(void)const{return reinterpret_cast<const toy::time_span*>(time_ptr);}};
	//	默认用 ms 来表示
	template <typename _Ty>
	typename _help::_time_type::trans_type<_Ty>::ret_type make_type(void)const
		{
		typedef typename _help::_time_type::trans_type<_Ty>::ret_type			rtv_type;
		typedef typename _help::_time_type::_cast_type<rtv_type>::rtv	cast_type;
		LOLIX_RETNV	rtv;
		time_info::span_type span_time;
		if ( LOLIX_IS_ERROR(rtv = _tif->get_span(&span_time, _tick)) )
			{
			LOLIX_ASSERT(!LOLIX_IS_ERROR(rtv), L"从时钟周期取时间的时候失败(err = 0x%I32x)", rtv);
			return 0;
			}

		return __down_cast(span_time, (rtv_type)_help::_time_type::trans_type<_Ty>::mul_dat, rtv_type());
		}

	template <typename _Tv, typename _Ty>
	LOLIX_INLINE_CALL	void set_time(_Tv v, _Ty /*y*/)
		{
		_tif->get_tick(&_tick, static_cast<time_info::span_type>(v*_help::_time_type::trans_type<_Ty>::mul_dat) );
		}

	LOLIX_INLINE_CALL	const LOLIX_RETNV	make_max(void)
		{
		_tick = __Tick_max();
		return	LOLIX_RETNV_OK;
		}

	LOLIX_INLINE_CALL	const LOLIX_RETNV	make_min(void)
		{
		_tick = __Tick_min();
		return LOLIX_RETNV_OK;
		}

	template<typename _Tyt>
	LOLIX_INLINE_CALL	explicit time_span(time_info* tif, _Tyt t)
		: _tif(tif)
		{ _tif->inc_ref(); _tif->get_tick(&_tick, t); }

	LOLIX_INLINE_CALL	explicit time_span(time_info* tif, time_max)
		: _tif(tif)
		, _tick(__Tick_max())
		{ _tif->inc_ref();}

	LOLIX_INLINE_CALL	explicit time_span(time_info* tif, time_zero)
		: _tif(tif)
		, _tick(0)
		{ _tif->inc_ref();}

	LOLIX_INLINE_CALL	explicit time_span(time_info* tif, time_min)
		: _tif(tif)
		, _tick(__Tick_min())
		{ _tif->inc_ref();}

	LOLIX_INLINE_CALL	time_span(time_info* tif)
		: _tif(tif)
		{ _tif->inc_ref(); }

	LOLIX_INLINE_CALL	time_span(const time_span& ts)
		: _tick(ts._tick)
		, _tif(ts._tif)
		{ _tif->inc_ref(); }


	template<typename _Tv, typename _Ty>
	LOLIX_INLINE_CALL	time_span(time_info* tif, _Tv v, _Ty)
		: _tif(tif)
		{
		_tif->inc_ref();
		_tif->get_tick(&_tick, static_cast<time_info::span_type>(v*_help::_time_type::trans_type<_Ty>::mul_dat) );
		}

	LOLIX_INLINE_CALL	~time_span(void)
		{
		_tif->release();
		}

	LOLIX_INLINE_CALL	time_span& operator = (time_max)
		{
		_tick = __Tick_max();
		return *this;
		}

	LOLIX_INLINE_CALL	time_span& operator = (time_zero)
		{
		_tick = 0;
		return *this;
		}

	LOLIX_INLINE_CALL	time_span& operator = (time_min)
		{
		_tick = __Tick_min();
		return *this;
		}

	LOLIX_INLINE_CALL	time_span& operator = (const time_span& t)
		{
		time_span const* const ps = &t;
		char const* const pc = (const char*)ps;
		//	如此迂回， 是为了解决 VS 2005 x64 Release 优化 链接时内部错误....
		switch ( (pc-(const char*)0 + 1)/4 )
			{
		case (time_min::time_ptr+1)/4:	_tick = __Tick_min();	break;
		case (time_zero::time_ptr+1)/4:	_tick = 0;				break;
		case (time_max::time_ptr+1)/4:	_tick = __Tick_max();	break;
		default:
				{
				_tick = t._tick;
				time_info* const old = _tif;
				(_tif = t._tif)->inc_ref();
				old->release();
				}
			break;
			}

		return *this;
		}

#if			LOLIX_CFG__IS_DEBUG
#define		__LOLIX__CHECK_IF_SAME_TIME_INFO__TMP_PROC__()	\
				do{\
					tick_type t0, t1;\
					LOLIX_CHECK_ERROR(_tif->get_tick(&t0, MB(1)));\
					LOLIX_CHECK_ERROR(t._tif->get_tick(&t1, MB(1)));\
					LOLIX_ASSERT(t0 == t1, L"进行比较的时间差单位不同");\
				}while(0)
#else	//	LOLIX_CFG__IS_DEBUG
#define		__LOLIX__CHECK_IF_SAME_TIME_INFO__TMP_PROC__()
#endif	//	LOLIX_CFG__IS_DEBUG

#define		__LX_DEF_OPT_INVOKE__(opt)	LOLIX_INLINE_CALL	bool operator opt (const time_span& t)const{__LOLIX__CHECK_IF_SAME_TIME_INFO__TMP_PROC__(); return _tick opt t._tick; }
	__LX_DEF_OPT_INVOKE__(<);
	__LX_DEF_OPT_INVOKE__(<=);
	__LX_DEF_OPT_INVOKE__(>);
	__LX_DEF_OPT_INVOKE__(>=);
	__LX_DEF_OPT_INVOKE__(==);
	__LX_DEF_OPT_INVOKE__(!=);
#undef		__LX_DEF_OPT_INVOKE__

	LOLIX_INLINE_CALL	const time_span operator + (time_span t)const	{ __LOLIX__CHECK_IF_SAME_TIME_INFO__TMP_PROC__(); time_span tmp(*this); return tmp+=t; }
	LOLIX_INLINE_CALL	time_span& operator += (time_span t)			{ __LOLIX__CHECK_IF_SAME_TIME_INFO__TMP_PROC__(); return this->operator +=( t._tick ); }
	LOLIX_INLINE_CALL	const time_span operator - (time_span t)const	{ __LOLIX__CHECK_IF_SAME_TIME_INFO__TMP_PROC__(); time_span tmp(*this); return tmp-=t; }
	LOLIX_INLINE_CALL	const time_span operator - (void)const			{												  time_span tmp(*this); tmp._tick = -tmp._tick; return tmp; }
	LOLIX_INLINE_CALL	time_span& operator -= (time_span t)			{ __LOLIX__CHECK_IF_SAME_TIME_INFO__TMP_PROC__(); return this->operator -=( t._tick ); }

#undef	__LOLIX__CHECK_IF_SAME_TIME_INFO__TMP_PROC__


	LOLIX_INLINE_CALL	const time_span operator * (long long m)const
		{
		time_span tmp(_tif);
		tmp._tick=_tick*m;
		return tmp;
		}

	friend LOLIX_INLINE_CALL const time_span operator * (int m, const time_span& t);

	LOLIX_INLINE_CALL	time_span& operator *= (long long m)
		{
		_tick*=m;
		return *this;
		}

	LOLIX_INLINE_CALL	void	get_tif(time_info** out_ptr)const
		{ *out_ptr = _tif; }

	LOLIX_INLINE_CALL	operator no_usd*(void)const
		{ return _tick?(no_usd*)1:(no_usd*)0; }

private:
	//const time_span operator - (long long)const;
	LOLIX_INLINE_CALL	time_span& operator -= (long long t)
		{
		if ( t < 0 )
			return this->operator += (-t);

		const span_type min_value = __Tick_min();
	//	min_value < value = _tick - t;
		if ( min_value + t > _tick )
			_tick = min_value;
		else
			_tick -= t;

		return *this;
		}

	//const time_span operator + (long long)const;
	LOLIX_INLINE_CALL	time_span& operator += (long long t)
		{
		if ( t < 0 )
			return this->operator -= (-t);
		const span_type max_time = __Tick_max();
		if ( max_time - t < _tick )
			_tick = max_time;
		else
			_tick += t;

		return *this;
		}

	LOLIX_INLINE_CALL	long long make_time(void)const
		{ return _tick; }

	friend time_type;


	LOLIX_INLINE_CALL	static	const tick_type	__Tick_max(void)
		{
		return type::type_value<decltype(((this_type*)0)->_tick)>::max_val;
		}
	LOLIX_INLINE_CALL	static	const tick_type	__Tick_min(void)
		{
		return type::type_value<decltype(((this_type*)0)->_tick)>::min_val;
		}

	template<typename _Ti, typename _To>
	LOLIX_INLINE_CALL	_To __down_cast(_Ti i, _To div, _To /*o*/)const
		{
		const _To omax = _To(0) - 1;
		const _Ti ri = i/div;
		if ( omax > 0 )
			{
			if ( ri > omax )
				return omax;
			if ( ri < 0 )
				return 0;
			}
		return _To(ri);
		}

	template<typename _Ti>
	LOLIX_INLINE_CALL	float __down_cast(_Ti v, float div, float /*f*/)const
		{
		return float(v/div);
		}

	template<typename _Ti>
	LOLIX_INLINE_CALL	double __down_cast(_Ti v, double div, double /*d*/)const
		{
		return double(v/div);
		}

	template<typename _Ti>
	LOLIX_INLINE_CALL	long double __down_cast(_Ti v, long double div, long double ld)const
		{
		return STATIC_CAST(ld, v/div);
		}

	span_type			_tick;
	mutable time_info*	_tif;
};
#pragma	pack(pop)
}}


namespace lolix{namespace toy{
class time_type
{
	typedef time_type this_type;
	struct no_used;

	time_info*		_tif;
	time_info::tick_type	_escap;
public:
	///	\return 返回系统所能够表示的最大的时间
	LOLIX_INLINE_CALL	time_type time_max(void)const
		{
		this_type tmp(_tif);
		tmp._escap = type::type_value<decltype(tmp._escap)>::max_val;
		return tmp;
		}
	LOLIX_INLINE_CALL	LOLIX_RETNV	make_current(void)
		{
		return _tif->cur_tick(&_escap);
		}

	LOLIX_INLINE_CALL	void	get_tif(time_info** out_ptr)const
		{ *out_ptr = _tif; }

	///	\brief 不做任何处理的时间
	LOLIX_INLINE_CALL	time_type(time_info* tif): _tif(tif){_tif->inc_ref();}
	LOLIX_INLINE_CALL	time_type(time_info* tif, time_info::tick_type tick): _tif(tif), _escap(tick){_tif->inc_ref();}
	LOLIX_INLINE_CALL	time_type( const time_type& o ): _tif(o._tif), _escap(o._escap){ _tif->inc_ref(); }

	///	\brief	初始化为 t 表示的时间
	/// \arg t	待初始化的时间
	LOLIX_INLINE_CALL	explicit time_type( const time_span& t)
		: _escap(t._tick)
		, _tif(t._tif)
	{ _tif->inc_ref(); }

	LOLIX_INLINE_CALL	time_type& operator = (const time_type& o)
		{
		_tif->release();
		(_tif = o._tif)->inc_ref();
		_escap = o._escap;
		return *this;
		}

	LOLIX_INLINE_CALL	~time_type(void){ _tif->release(); }

	/**
	* \brief	将当前时间减去另一个时间
	* \arg t	另一个时间
	* \return	相对另一个时间的时间差, 可能小于0
	**/
	LOLIX_INLINE_CALL	const time_span operator - (const time_type& t)const
		{
		time_span tmp(_tif);
		STATIC_COPY(tmp._tick, _escap - t._escap);
		return tmp;
		}

	/**
	* \brief	将当前时间减去一个时间间隔
	* \arg t	时间间隔
	* \return	相距指定时间间隔的时间, 可能小于0
	**/
	LOLIX_INLINE_CALL	const time_type operator - (const time_span& t)const
		{
		time_type tmp(_tif);
		STATIC_COPY(tmp._escap, _escap - t._tick);
		return tmp;
		}

	/**
	* \brief	依照差距值调整自己的时间
	* \return	自己(调整之后的时间)
	* \arg t	相对时间
	**/
	LOLIX_INLINE_CALL	time_type& operator += (const time_span& t)
		{
		u_type<decltype(t._tick), decltype(_escap)> udata(t._tick);
		_escap += udata.second;
		return *this;
		}

	/**
	* \brief	计算当前时间加上一个时间差后的时间
	* \arg t	相对时间
	* \return	相对某个时间后的某个时间
	**/
	LOLIX_INLINE_CALL	const time_type operator + (const time_span& t)const
		{
		time_type tmp(*this);
		return tmp+=t;
		}

	friend const time_type operator + (const time_span& ts, time_type tt);

	///////////////////////////////////////////////
	//	#	:比较自己和传入的时间
	//	@	:某个时间
#define	__LX_DEF_OPT_INVOKE__(opt)	LOLIX_INLINE_CALL	bool	operator opt (const this_type& o)const{ return _escap opt o._escap; }
	__LX_DEF_OPT_INVOKE__(<);
	__LX_DEF_OPT_INVOKE__(<=);
	__LX_DEF_OPT_INVOKE__(>);
	__LX_DEF_OPT_INVOKE__(>=);
	__LX_DEF_OPT_INVOKE__(==);
	__LX_DEF_OPT_INVOKE__(!=);
#undef	__LX_DEF_OPT_INVOKE__

	/**
	* \brief 判断时间是不是为0
	* \return	如果为0，则返回空指针，否则返回非空指针
	**/
	LOLIX_INLINE_CALL	operator no_used*(void)const
		{
		return _escap?(no_used*)1: 0;
		}
};
}}


namespace lolix{namespace toy{
/**
* \brief	计算一个时间差与另一个时间相加后的值
* \arg	ts	相对时间
* \arg	tt	某个时间
* \return	相对某个时间后的某个时间
**/
LOLIX_INLINE_CALL	const time_type	operator + (const time_span& ts, time_type tt)
	{
	time_type tmp(tt);
	return tt+=ts;
	}

/**
* \brief	计算一个时间间隔乘一个倍数之后的结果
* \arg	m	相乘的倍数
* \arg	ts	待相乘的时间差
* \return	经过乘计算后的时间差值
**/
LOLIX_INLINE_CALL	const time_span operator*(int m, const time_span& ts)
	{ return ts*m; }


LOLIX_INLINE_CALL	time_type	time_current(time_info* tif)
	{
	time_type tt(tif);
	tt.make_current();
	return tt;
	}


}}


#endif	//	!defined(__LOLIX__TOY__TIME_TYPES__K_IN_G__)
